/*
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

/*
 *
 *
 *
 *
 *
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package java.util.concurrent.atomic;

import java.util.function.IntUnaryOperator;
import java.util.function.IntBinaryOperator;

import sun.misc.Unsafe;


/**
 * 什么情况下使用AtomicInteger?
 * 1、作为多个线程同时使用的原子计数器。
 * 2、在比较和交换操作中实现非阻塞算法。
 */
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    //unsafe常量，设置为使用Unsafe.compareAndSwapInt进行更新
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    /**
     * 1、保证变量在线程间可见，对volatile变量所有的写操作都能立即反应到其他线程中，
     *   换句话说，volatile变量在各个线程中是一致的（得益于java内存模型—"先行发生原则"）
     * 2、禁止指令的重排序优化
     */
    private volatile int value;


    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }


    // AtomicInteger获取值

    /**
     * 获取当前 AtomicInteger 的值
     */
    public final int get() {
        return value;
    }

    /**
     * 设置当前 AtomicInteger 的值
     */
    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    /**
     * 获取当前值，并设置新值，这个操作是原子性的，即在多线程环境下，任何时刻只有一个线程能够执行这个操作。
     */
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }


    // AtomicInteger作为原子计数器

    /**
     * 以原子方式将给定值添加到当前值,并在添加后返回新值
     */
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }

    /**
     * 以原子方式将给定值添加到当前值并返回旧值
     */
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    /**
     * 以原子方式将当前值递增1并在递增后返回新值。它相当于i ++操作
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    /**
     * 以原子方式递增当前值并返回旧值。它相当于++ i操作
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    /**
     * 原子地将当前值减1并在减量后返回新值。它等同于i-操作
     */
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

    /**
     * 以原子方式递减当前值并返回旧值。它相当于-i操作
     */
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }


    // 比较和交换操作

    /**
     * 比较当前AtomicInteger对象的值和一个期望值
     * 如果相等，就将AtomicInteger对象的值修改为一个新值
     * 同时，这个操作是原子性的，保证在多线程的情况下，只有一个线程能够成功修改AtomicInteger对象的值。
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    /**
     * 无锁的原子操作,用于比较并设置操作。该方法比较当前AtomicInteger的值与给定的预期值，
     * 如果相等，则将AtomicInteger的值设为新值，并返回true;否则不作任何操作，返回false
     * 与compareAndSet方法相比，weakCompareAndSet方法不保证在高并发环境下的操作的原子性，但可以提高系统的并发性能
     */
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }



    // 支持 Function 方法

    /**
     * 使用给定的更新函数原子地更新并返回旧值
     * 该方法确保在多线程环境下对变量的更新是原子操作，防止了数据不一致的问题
     * @param updateFunction 一个接受int参数并返回int的新值的函数
     * @return 返回更新前的值
     */
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        // 使用CAS(Compare And Swap)操作来保证线程安全
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }


    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }


    public final int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }


    public final int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }


    public String toString() {
        return Integer.toString(get());
    }

    public int intValue() {
        return get();
    }

    public long longValue() {
        return (long) get();
    }

    public float floatValue() {
        return (float) get();
    }

    public double doubleValue() {
        return (double) get();
    }

}
